/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\mkouiiablist.h"

//---------------------------------------------------------------------------

MKOuiIabList::MKOuiIabList(void) : MFileConfig()
{
	processOui = false;
	processIab = false;

	//UpdateVendorList();
}

MKOuiIabList::~MKOuiIabList(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

void MKOuiIabList::CleanUp(void)
{
	MKOuiIab *elem;

	for(itor=theVector.begin(); itor!=theVector.end(); itor++){
		elem = *itor;
		if(elem!=NULL){
			*itor = NULL;
			delete elem;
		}
	}
	theVector.clear();
}

//---------------------------------------------------------------------------

MKOuiIab* MKOuiIabList::CreateElement(void)
{
	MKOuiIab *elem;

	try{
		elem = new MKOuiIab;
	}
	catch(...){
		elem = NULL;
	}
	return elem;
}

bool MKOuiIabList::AddElement(MKOuiIab* elemIn)
{
	MKOuiIab *elem;
	int cnt;

	if(elemIn==NULL) return true;

	elem = CreateElement();
	if(elem==NULL) return false;

	elem->LoadFromPointer(elemIn);

	cnt = (int)theVector.size();
	theVector.push_back(elem);
	cnt = (int)theVector.size() - cnt;
	if(cnt!=1){
		delete elem;
		return false;
	}

	return true;
}

bool MKOuiIabList::AddElement(MAddressMAC* msIn, MAddressMAC* meIn, CString vendorIn, bool isOuiIn)
{
	MKOuiIab elem;

	if(msIn==NULL) return false;
	if(meIn==NULL) return false;

	elem.macStart.LoadFromPointer(msIn);
	elem.macEnd.LoadFromPointer(meIn);
	elem.vendorName = vendorIn;
	elem.isOui      = isOuiIn;

	return AddElement(&elem);
}

//---------------------------------------------------------------------------

void MKOuiIabList::Initialize(void)
{
	MAddressMAC macS;
	MAddressMAC macE;

	CleanUp();

	// TODO this is somehow WRONG because 00-00-5E belongs to USC INFORMATION SCIENCES INST (oui.txt)

	/*
	macS.LoadFromString(_T("00-00-5E-00-00-00"));
	mace.LoadFromString(_T("00-00-5E-00-00-FF"));
	AddElement(&macS, &macE, _T("U: [IANA Reserved]");

	macS.LoadFromString(_T("00-00-5E-00-01-00"));
	mace.LoadFromString(_T("00-00-5E-00-01-FF"));
	AddElement(&macS, &macE, _T("VRRP [RFC3768]");

	macS.LoadFromString(_T("00-00-5E-00-02-00"));
	mace.LoadFromString(_T("00-00-5E-FF-FF-FF"));
	AddElement(&macS, &macE, _T("[IANA Unicast]");
	*/
}

//---------------------------------------------------------------------------

bool MKOuiIabList::Add(MKOuiIab* miIn)
{
	return AddElement(miIn);
}

//---------------------------------------------------------------------------

void MKOuiIabList::Clear(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

int MKOuiIabList::GetSize(void)
{
	return (int)theVector.size();
}

MKOuiIab* MKOuiIabList::GetByIdx(int idxIn)
{
	if(idxIn<0)          return NULL;
	if(idxIn>=GetSize()) return NULL;

	return theVector[idxIn];
}

//---------------------------------------------------------------------------

CString MKOuiIabList::GetVendorName(MAddressMAC* macIn, bool reportUnknown)
{
	MKOuiIab *elem;
	MAddressMAC macS;
	CString str;
	bool found;

	str.Empty();

	if(macIn==NULL) return str;

	macS.LoadFromPointer(macIn);

	// 00-00-5E-00-00-00 to 00-00-5E-00-00-FF [IANA Reserved]
	// 00-00-5E-00-01-00 to 00-00-5E-00-01-FF VRRP [RFC3768]
	// 00-00-5E-00-02-00 to 00-00-5E-FF-FF-FF [IANA Unicast]
	if(macS.addr[0]==0x00 && macS.addr[1]==0x00 && macS.addr[2]==0x5E){
		// IANA Unicast
		if(macS.addr[3]!=0x00){
			str = _T("- IANA Unicast -");
			return str;
		}
		switch(macS.addr[4]){
			case 0x00 :
				str = _T("- IANA Reserved -");
				break;
			case 0x01 :
				str = _T("- VRRP -");
				break;
			default :
				str = _T("- IANA Unicast -");
				break;
		}
		return str;
	}

	// IANA Internet Multicast: 01-00-5E-00-00-00  to  01-00-5E-7F-FF-FF
	// IANA Internet Reserved: 01-00-5E-80-00-00  to  01-00-5E-FF-FF-FF
	if(macS.addr[0]==0x01 && macS.addr[1]==0x00 && macS.addr[2]==0x5E){
		if(macS.addr[3]<0x80) str = _T("M: IANA Internet Multicast");
		else                  str = _T("M: IANA Internet Reserved");
		return str;
	}

	if(macS.addr[0]==0x01){
		if(macS.addr[1]==0x80){
			if(macS.addr[2]==0xc2){
				if(macS.addr[3]==0x00){
					if(macS.addr[4]==0x00){
						switch(macS.addr[5]){
							case 0x20 :
								str = _T("GMRP - GARP Multicast Registration Protocol");
								return str;
							case 0x21 :
								str = _T("GVRP - GARP VLAN Registration Protocol");
								return str;
						}
					}
				}
			}
		}
	}

	if(macS.addr[0]==0x33 && macS.addr[1]==0x33){
		if(macS.addr[2]==0 && macS.addr[3]==0 && macS.addr[4]==0){
			switch(macS.addr[5]){
				case 0x01:
					str = _T("IPv6's all-nodes multicast address");
					break;
				case 0x02:
					str = _T("IPv6's all-routers multicast address");
					break;
				default:
					str = _T("IPv6 multicast address");
					break;
			}
		}
		else{
			str = _T("%s is an IPv6 multicast address");
		}
		return str;
	}

	if(macS.IsBroadcastAddress()){
		str = _T("- broadcast -");
		return str;
	}

	if(macS.IsEmpty()){
		str = _T("- empty -");
		return str;
	}

	if(macS.IsMulticastAddress()){
		macS.addr[0] &= 0xFE;		///< Clear multicast bit
		str = _T("M:");
	}

	found = false;
	for(itor=theVector.begin(); itor!=theVector.end() && !found; itor++){
		elem = *itor;
		if(elem!=NULL){
			if(elem->MACIsMember(&macS)){
				str += elem->vendorName;
				found = true;
			}
		}
	}

	if(!found)
		if(reportUnknown)
			str += _T("- unknown -");

	return str;
}

//---------------------------------------------------------------------------

CString MKOuiIabList::GetVendorName_MACLookup(MAddressMAC* macIn)
{
	MKOuiIab *elem;
	MAddressMAC macS;
	CString str;
	bool found, mcast;

	if(macIn==NULL){
		// SHOULD NOT happend !
		str = _T("MAC Lookup: invalid parameter !");
		return str;
	}

	macS.LoadFromPointer(macIn);

	// 00-00-5E-00-00-00 to 00-00-5E-00-00-FF [IANA Reserved]
	// 00-00-5E-00-01-00 to 00-00-5E-00-01-FF VRRP [RFC3768]
	// 00-00-5E-00-02-00 to 00-00-5E-FF-FF-FF [IANA Unicast]
	if(macS.addr[0]==0x00 && macS.addr[1]==0x00 && macS.addr[2]==0x5E){
		if(macS.addr[3]!=0x00){
			str.Format(_T("%s belongs to IANA Unicast"), macS.GetS());
			return str;
		}
		switch(macS.addr[4]){
			case 0x00 :
				str.Format(_T("%s belongs to IANA Reserved"), macS.GetS());
				break;
			case 0x01 :
				str.Format(_T("%s belongs to VRRP (RFC 3768)"), macS.GetS());
				break;
			default :
				str.Format(_T("%s belongs to IANA Unicast"), macS.GetS());
				break;
		}
		return str;
	}

	// IANA Internet Multicast: 01-00-5E-00-00-00  to  01-00-5E-7F-FF-FF
	// IANA Internet Reserved: 01-00-5E-80-00-00  to  01-00-5E-FF-FF-FF
	if(macS.addr[0]==0x01 && macS.addr[1]==0x00 && macS.addr[2]==0x5E){
		if(macS.addr[3]<0x80) str.Format(_T("%s belongs to IANA Internet Multicast"), macS.GetS());
		else                  str.Format(_T("%s belongs to IANA Internet Reserved"),  macS.GetS());
		return str;
	}

	if(macS.addr[0]==0x01){
		if(macS.addr[1]==0x80){
			if(macS.addr[2]==0xc2){
				if(macS.addr[3]==0x00){
					if(macS.addr[4]==0x00){
						switch(macS.addr[5]){
							case 0x20 :
								str.Format(_T("%s belongs to GMRP (GARP Multicast Registration Protocol)"), macS.GetS());
								return str;
							case 0x21 :
								str.Format(_T("%s belongs to GVRP (GARP VLAN Registration Protocol)"), macS.GetS());
								return str;
						}
					}
				}
			}
		}
	}

	if(macS.addr[0]==0x33 && macS.addr[1]==0x33){
		if(macS.addr[2]==0 && macS.addr[3]==0 && macS.addr[4]==0){
			switch(macS.addr[5]){
				case 0x01:
					str.Format(_T("%s is the IPv6's all-nodes multicast address"), macS.GetS());
					break;
				case 0x02:
					str.Format(_T("%s is the IPv6's all-routers multicast address"), macS.GetS());
					break;
				default:
					str.Format(_T("%s is an IPv6 multicast address"), macS.GetS());
					break;
			}
		}
		else{
			str.Format(_T("%s is an IPv6 multicast address"), macS.GetS());
		}
		return str;
	}

	if(macS.IsBroadcastAddress()){
		str.Format(_T("%s is the broadcast MAC"), macS.GetS());
		return str;
	}

	if(macS.IsEmpty()){
		str.Format(_T("%s is the empty MAC"), macS.GetS());
		return str;
	}

	if(macS.IsMulticastAddress()){
		macS.addr[0] &= 0xFE;		///< Clear multicast bit
		mcast = true;
	}
	else{
		mcast = false;
	}

	found = false;
	for(itor=theVector.begin(); itor!=theVector.end() && !found; itor++){
		elem = *itor;
		if(elem!=NULL){
			if(elem->MACIsMember(&macS)){
				if(mcast){
					macS.addr[0] |= 0x01;
					str.Format(_T("The vendor for %s is %s (multicast)"), macS.GetS(), elem->vendorName);
				}
				else{
					str.Format(_T("The vendor for %s is %s"), macS.GetS(), elem->vendorName);
				}
				found = true;
			}
		}
	}

	if(!found){
		if(mcast){
			macS.addr[0] |= 0x01;
			str.Format(_T("%s is unknown (multicast)"), macS.GetS());
		}
		else{
			str.Format(_T("%s is unknown"), macS.GetS());
		}
	}

	return str;
}

//---------------------------------------------------------------------------

bool MKOuiIabList::LoadFromOuiFile(CString strIn)
{
	processOui = true;
	processIab = false;

	return LoadFromFile(strIn);
}

bool MKOuiIabList::LoadFromIabFile(CString strIn)
{
	processOui = false;
	processIab = true;

	return LoadFromFile(strIn);
}

bool MKOuiIabList::LoadFromMyFile(CString strIn)
{
	processOui = false;
	processIab = false;

//	DWORD ts, te;
	bool res;

//	ts = GetTickCount();
	res = LoadFromFile(strIn);
//	te = GetTickCount();
//	te -= ts;

//	CString str;
//	str.Format(_T("--- Load time: %d\n"), te);
//	OutputDebugString(str);

	return res;
}

//---------------------------------------------------------------------------

void MKOuiIabList::InternalInit(void)
{
	if(!processOui && !processIab)
		Initialize();
}

//---------------------------------------------------------------------------

bool MKOuiIabList::InternalProcessOuiLine(char* buf)
{
	CString str;
	CString sss;
	CString sMac;
	MAddressMAC macS;
	MAddressMAC macE;
	int pos;

	str = buf;

	if(str.GetLength()<8) return true;

	pos = str.Find('-', 0);
	if(pos!=2) return true;
	pos = str.Find('-', 3);
	if(pos!=5) return true;
	pos = str.Find(')', 0);
	if(pos==-1) return true;

	sss = str.Mid(0, 8);

	if(sss.CompareNoCase(_T("00-50-C2"))==0){
		// IAB space, ignore, it will be loaded separated
		return true;
	}

	sMac = sss + _T("-00-00-00");
	if(!macS.LoadFromString(sMac)) return false;
	sMac = sss + _T("-FF-FF-FF");
	if(!macE.LoadFromString(sMac)) return false;

	sss = str.Mid(pos+1);
	sss = sss.Trim();
	if(sss.IsEmpty())
		sss = _T("PRIVATE");

	return AddElement(&macS, &macE, sss, true);
}

bool MKOuiIabList::InternalProcessIabLine(char* buf)
{
	CString str;
	CString sss1, sss2, sss3;
	CString sMac;
	MAddressMAC macS;
	MAddressMAC macE;
	int pos;

	str = buf;

	if(str.GetLength()<13) return true;

	pos = str.Find('-', 0);
	if(pos!=6) return true;

	pos = str.Find(')', 0);
	if(pos==-1) return true;

	sss1 = str.Mid(0, 2);
	sss2 = str.Mid(2, 2);
	sss3 = str.Mid(4, 2);
	sMac = _T("00-50-C2-");
	sMac += sss1;
	sMac += _T("-");
	sMac += sss2;
	sMac += _T("-");
	sMac += sss3;
	if(!macS.LoadFromString(sMac)) return false;

	sss1 = str.Mid(7, 2);
	sss2 = str.Mid(9, 2);
	sss3 = str.Mid(11, 2);
	sMac = _T("00-50-C2-");
	sMac += sss1;
	sMac += _T("-");
	sMac += sss2;
	sMac += _T("-");
	sMac += sss3;
	if(!macE.LoadFromString(sMac)) return false;

	sMac = str.Mid(pos+1);
	sMac = sMac.Trim();
	if(sMac.IsEmpty())
		sMac = _T("PRIVATE");

	return AddElement(&macS, &macE, sMac, false);
}

bool MKOuiIabList::InternalProcessLine(char* buf)
{
	CString str;
	LPCTSTR alfa;

	if(buf==NULL){
		// nothing to process
		return true;
	}

	if(processOui) return InternalProcessOuiLine(buf);
	if(processIab) return InternalProcessIabLine(buf);

	CString strS, strE;
	MAddressMAC macS;
	MAddressMAC macE;

	str = buf;
	//str = str.Trim();
	alfa = str;

	switch(alfa[0]){
		case 'o':
		case 'O':
			if(str.GetLength()<9) return false;

			strS = str.Mid(1, 8);
			strE = strS;
			strS += _T("-00-00-00");
			if(!macS.LoadFromString(strS)) return false;
			strE += _T("-FF-FF-FF");
			if(!macE.LoadFromString(strE)) return false;
	
			str = &alfa[9];
			str = str.Trim();
			if(str.IsEmpty())
				str = _T("PRIVATE");
			return AddElement(&macS, &macE, str, true);

		case 'i':
		case 'I':
			if(str.GetLength()<14) return false;

			strS = str.Mid(1, 13);
			strE = strS;
			strS += _T("0-00");
			if(!macS.LoadFromString(strS)) return false;
			strE += _T("F-FF");
			if(!macE.LoadFromString(strE)) return false;
	
			str = &alfa[14];
			str = str.Trim();
			if(str.IsEmpty())
				str = _T("PRIVATE");
			return AddElement(&macS, &macE, str, false);

		default:
			return false;
	}
}

//---------------------------------------------------------------------------

bool MKOuiIabList::InternalSave(void)
{
	MKOuiIab *elem;
	CString str;

	if(!WriteEmptyCommentLine()) return false;
	if(!WriteCommentLine(_T("CHScanner - Vendors by MAC"))) return false;
	if(!WriteEmptyCommentLine()) return false;
	if(!WriteCommentLine(_T("Builded from the \"oui.txt\" and \"iab.txt\" files downloaded from http://standards.ieee.org/regauth/oui/index.shtml"))) return false;
	if(!WriteCommentLine(_T("As stated there, the \"oui.txt\" and \"iab.txt\" files where generated on 17 January 2006"))) return false;
	if(!WriteEmptyCommentLine()) return false;

	if(!WriteEmptyLine()) return false;

	for(itor=theVector.begin(); itor!=theVector.end(); itor++){
		elem = *itor;
		if(elem!=NULL){
			if(elem->isOui){
				str = _T("O");
				str += elem->macStart.GetSD();
				str = str.Left(9);
				str += elem->vendorName;
			}
			else{
				str = _T("I");
				str += elem->macStart.GetSD();
				str = str.Left(14);
				str += elem->vendorName;
			}
			if(!WriteLine(str)) return false;
		}
	}

	if(!WriteEmptyLine()) return false;

	return true;
}

//---------------------------------------------------------------------------

bool MKOuiIabList::UpdateVendorList(void)
{
	bool res;
	res = LoadFromOuiFile("C:\\temp\\oui.txt");
	res = LoadFromIabFile("C:\\temp\\iab.txt");
	res = SaveToFile("C:\\temp\\ouiiab.txt");
	res = LoadFromMyFile("C:\\temp\\ouiiab.txt");
	res = SaveToFile("C:\\temp\\vendors.dat");
	return res;
}

//---------------------------------------------------------------------------
